/*
 * @(#)XfvhdlArithCalcMemFiles.java        3.0                2004/09/14
 *
 * This file is part of Xfuzzy 3.0, a design environment for fuzzy logic
 * based systems.
 *
 * (c) 2000 IMSE-CNM. The authors may be contacted by the email address:
 *                    xfuzzy-team@imse.cnm.es
 *
 * Xfuzzy is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation.
 *
 * Xfuzzy is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */

package xfuzzy.xfvhdl;

import xfuzzy.*;
import xfuzzy.lang.*;

/**
* Clase que genera el fichero con la memoria de c�lculo aritm�tico 
* ArithCalcMem.vhdl.
* @author Jos� Mar�a �vila Maireles, <b>e-mail</b>: josavimai@alum.us.es
* @version 3.0
*/
public class XfvhdlArithCalcMemFiles {

	// El siguiente atributo es para las funciones miembro de las 
	// variables de entrada ordenadas 
	private XfvhdlInOrderParamMemFunc[] inOrderMfInput;
	// El siguiente atributo se usa para crear el archivo .prj
	private XfvhdlPrjFile prjFile;
	// El siguiente atributo se usa para mostrar los mensajes por pantalla.
	private XfvhdlMessage msg;

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	//			  CONSTRUCTOR DE LA CLASE				          
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// 
	/** Constructor de la clase.
	 */
	XfvhdlArithCalcMemFiles(XfvhdlPrjFile pF, Xfuzzy xf) {
		prjFile = pF;
		msg = new XfvhdlMessage(xf);
	}

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	//			  M�TO_DOS P�BLICOS DE LA CLASE				      
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	/**
	* M�todo que genera el fichero con la memoria de c�lculo aritm�tico 
	* ArithCalcMem.vhdl.
	*/
	public void createArithCalcMem(Specification spec) {
		Variable[] var = spec.getSystemModule().getInputs();
		XfvhdlError error = new XfvhdlError();
		String state = new String("Done");

		msg.addMessage(
			"\n>>>> Generating file:  "
				+ XfvhdlProperties.outputFile
				+ "ArithCalcMem.vhdl");
		msg.show();

		// Creo el fichero ArithCalcMem.vhdl.
		new XfvhdlCreateFile(
			XfvhdlProperties.outputFile + "ArithCalcMem.vhdl",
			createMemorySource(var));
		prjFile.addFile(XfvhdlProperties.outputFile + "ArithCalcMem.vhdl");

		if (error.hasErrors())
			state = "With error";
		if (XfvhdlProperties.complementaryFiles == true) {
			msg.addMessage(
				"  (with complementary files)     ......... " + state);
			msg.show();
		} else {
			msg.addMessage("     ......... " + state);
			msg.show();
		}
	}

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	//			  M�TO_DOS PRIVADOS DE LA CLASE				        
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//

	/**
	* M�todo que crea la cadena que ser� escrita en fichero 
	* ArithCalcMem.vhdl.
	* @return Devuelve la cadena que ser� escrita en fichero 
	* ArithCalcMem.vhdl.
	*/
	private String createMemorySource(Variable[] var) {

		XfvhdlHeadFile head =
			new XfvhdlHeadFile(
				XfvhdlProperties.fileDir,
				XfvhdlProperties.outputFile + "ArithCalcMem.vhdl",
				XfvhdlProperties.ficheroXFL);

		String code = head.getHead();

		code
			+= "\n--***********************************************************************--\n"
			+ "--                                                                       --\n"
			+ "--   DESCRIPTION: This file contains the VHDL description for the        --\n"
			+ "--                arithmetic calculus memory of the fuzzy controller.    --\n"
			+ "--                                                                       --\n"
			+ "---------------------------------------------------------------------------\n"
			+ "--                                                                       --\n"
			+ "--   AUTHOR:      Jose Maria Avila Maireles                              --\n"
			+ "--                                                                       --\n"
			+ "--   VERSION:     Xfvhdl  ver1.2                            April 2004   --\n"
			+ "--                                                                       --\n"
			+ "--***********************************************************************--\n"
			+ "\n"
			+ "\n"
			+ "library IEEE;\n"
			+ "use IEEE.std_logic_1164.all;\n"
			+ "\n"
			+ "use WORK.Constants.all;\n"
			+ "\n"
			+ "\n"
			+ "---------------------------------------------------------------------------\n"
			+ "--                           Entity description                          --\n"
			+ "---------------------------------------------------------------------------\n"
			+ "\n"
			+ "entity ArithCalcMem is\n"
			+ "\n"
			+ "\tport(\taddr\t: in std_logic_vector(bits_etiq-1 downto 0);"
			+ "\t-- Address bus\n"
			+ "\t\tme\t: in std_logic;"
			+ "\t\t\t\t\t\t-- Memory enable\n"
			+ "\n"
			+ "\t\tdo\t: out std_logic_vector(M-1 downto 0));"
			+ "\t\t-- Data bus\n"
			+ "end ArithCalcMem;\n"
			+ "\n"
			+ "\n"
			+ "---------------------------------------------------------------------------\n"
			+ "--                       Architecture description                        --\n"
			+ "---------------------------------------------------------------------------\n"
			+ "\n"
			+ "architecture FPGA of ArithCalcMem is\n"
			+ "\n"
			+ "\tsignal data: std_logic_vector(M downto 1);\n"
			+ "\n"
			+ "begin\n"
			+ "\n"
			+ "\tProceso_Lectura: process(me, addr)\n"
			+ "\t\tbegin\n"
			+ "\n"
			+ "\t\t\tif (me = '1' and not me'stable) then\n"
			+ "\t\t\t\tcase addr is\n";

		XfvhdlBinaryDecimal converter = new XfvhdlBinaryDecimal();
		int total = var.length;
		double x1, x2, pendiente_bin;

		// Creo la estructura inOrderMfInput[i], donde con el 
		// �ndice i direcciono las distintas variables de entrada, 
		// donde contengo una lista con sus funciones miembro ordenadas.		
		inOrderMfInput = new XfvhdlInOrderParamMemFunc[total];
		for (int i = 0; i < total; i++) {
			inOrderMfInput[i] = new XfvhdlInOrderParamMemFunc(var[i]);
			inOrderMfInput[i].sort();
		}

		// AHORA SE COMPRUEBAN TODAS LAS RESTRICCIONES QUE LA
		// ESPECIFICACI�N XFL3 DEBE CUMPLIR
		// Todas las entradas tienen que tener el mismo n� de mf
		int nmf = inOrderMfInput[0].getSize();
		for (int i = 0; i < total; i++) {
			if (inOrderMfInput[i].getSize() != nmf) {
				new XfvhdlError(3);
				return "-- ...Error found. Not a valid variable!";
			}
		}

		// Todas las mf's deben ser triangulares
		for (int i = 0; i < total; i++) {
			for (int i2 = 0; i2 < nmf; i2++)
				if (inOrderMfInput[i].getParamMemFunc(i2).get().length != 3) {
					new XfvhdlError(30);
					return "-- ...Error found. Not a valid variable!";
				}
		}

		// Las mf's deben ser complementarias entre s� y la segunda de
		// cada variable debe tener su primer punto situado en el valor
		// m�nimo del universo de discurso.
		double tmp[], tmp2[];
		for (int i = 0; i < nmf - 1; i++) {
			for (int i2 = 0; i2 < XfvhdlProperties.entradas; i2++) {
				tmp = inOrderMfInput[i2].getParamMemFunc(i).get();
				tmp2 = inOrderMfInput[i2].getParamMemFunc(i + 1).get();
				if (tmp[1] != tmp2[0]) {
					new XfvhdlError(30);
					return "-- ...Error found. Not a valid variable!";
				}
			}
		}

		double x1_bin, x2_bin, universe_max, universe_min;
		for (int i = 1; i < nmf; i++) {
			// No contemplo las mfs que empiecen antes que el universo de 
			// discurso
			//tmp = inOrderMfInput[0].getParamMemFunc(i).get();

			code += "\t\t\t\t\twhen \""
				+ converter.toBinary(i - 1, XfvhdlProperties.bits_etiq)
				+ "\" => data <= \"";
			for (int i2 = 0; i2 < XfvhdlProperties.entradas; i2++) {
				tmp = inOrderMfInput[i2].getParamMemFunc(i).get();
				x1 = tmp[0];
				x2 = tmp[1];

				universe_max = var[i2].getType().getUniverse().max();
				universe_min = var[i2].getType().getUniverse().min();

				x1_bin = PasarDecimalBinario(1, 1, 0, 6);
				x1_bin =
					PasarDecimalBinario(
						x1,
						universe_max,
						universe_min,
						XfvhdlProperties.N);
				x2_bin =
					PasarDecimalBinario(
						x2,
						universe_max,
						universe_min,
						XfvhdlProperties.N);

				// Esto esta todo pendiente de probar.
				// Y OJO, en PasarDecimalBinario, lo correcto ser�a:
				// pendiente_bin = PasarDecimalBinario(pendiente,0,1,P),
				// pero aparecen valores negativos de la pendiente, as� que
				// por eso aparecen cambiados estos valores a (pendiente,1,0,P)
				pendiente_bin =
					PasarDecimalBinario(
						CalcularPendienteRecta(x1_bin, 0.0, x2_bin, 1.0),
						1.0,
						0.0,
						XfvhdlProperties.P);

				code += ""
					+ converter.toBinary((int) x1_bin, XfvhdlProperties.N)
					+ converter.toBinary((int) pendiente_bin, XfvhdlProperties.P);

				// bloque de c�digo para depuraci�n
				/*
				   code += " " + x1_bin + " " + pendiente_bin + " ";
				   code += "**"
				      + converter.toBinary((int) x1_bin, XfvhdlProperties.N)
				      + "**"
				      + converter.toBinary((int) pendiente_bin, XfvhdlProperties.P);
				*/

				// El siguiente c�digo es lo que hab�a en la versi�n 2.2
				/*
				x1_bin = PasarDecimalBinario(p_dato->Valor,
				                             tabla_tipo[i]->Valor_Minimo,
				                             tabla_tipo[i]->Valor_Maximo, N);
				
				x2_bin = PasarDecimalBinario(p_dato->p_siguiente->Valor,
				                             tabla_tipo[i]->Valor_Minimo,
				                             tabla_tipo[i]->Valor_Maximo, N);
				
				recta = CalcularRecta(x1_bin, 0.0, x2_bin, 1.0);
				
				pendiente_bin = PasarDecimalBinario(recta.Pendiente,
				                                    0.0, 1.0, P);
				
				fprintf(fichero, "%s%s", CodificarBinario((int) x1_bin, N),
				                         CodificarBinario((int) pendiente_bin, P));
				 */

			}
			code += "\";\n";

		}

		code += "\t\t\t\t\twhen others => data <= \""
			+ converter.toHyphen(
				(XfvhdlProperties.N + XfvhdlProperties.P)
					* XfvhdlProperties.entradas)
			+ "\";\n";
		code += "\t\t\t\tend case;\n"
			+ "\t\t\tend if;\n"
			+ "\n"
			+ "\tend process Proceso_Lectura;\n\n"
			+ "\tdo <= data;\n\n"
			+ "end FPGA;";

		return code;
	}

	double CalcularPendienteRecta(double x1, double y1, double x2, double y2) {
		double pendiente;

		if (x1 == x2) {
			XfvhdlError err = new XfvhdlError(19);
			err.show();
		}

		pendiente = (y2 - y1) / (x2 - x1);

		return (pendiente);
	}

	double PasarDecimalBinario(
		double valor,
		double max,
		double min,
		int precision) {
		double max_bin;
		int resultado;

		max_bin = Math.pow(2.0, precision) - 1;

		resultado = (int) ((max_bin * (valor - min)) / (max - min));
		return (resultado);
	}

	// El siguiente c�digo es lo que hab�a en la versi�n 2.2 
	/*
	float PasarDecimalBinario(valor, min, max, precision)
	float valor;
	float min;
	float max;
	unsigned int precision;
	   {
	    float max_bin;
	    int resultado;
	
	    max_bin = Potencia(2.0, (double) precision) - 1;
	
	    resultado = (int) ((max_bin * (valor - min))/(max - min));
	    return((float) resultado);
	   }
	 */

} // Fin de la clase
